home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / readers / nn-tk.001 / nn-tk~ / nn / sort.c < prev    next >
C/C++ Source or Header  |  1996-03-11  |  12KB  |  461 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Article sorting.
  5.  */
  6.  
  7. #include "config.h"
  8. #include "articles.h"
  9.  
  10. /* sort.c */
  11.  
  12. static order_subj_date __APROTO((article_header **ah1, article_header **ah2));
  13. static order_date_subj_date __APROTO((article_header **ah1, article_header **ah2));
  14. static order_arrival __APROTO((article_header **a, article_header **b));
  15. static order_date __APROTO((register article_header **ah1, register article_header **ah2));
  16. static order_from_date __APROTO((register article_header **ah1, register article_header **ah2));
  17. static flag_type article_equal __APROTO((article_header **ah1, article_header **ah2));
  18.  
  19. static int order_arrival();
  20.  
  21. #ifdef BAD_ORDER_SUBJ_DATE
  22. /* If one article's subject is identical to the first part of another
  23.  article, the two subjects will still be considered identical if the
  24.  length of the shorter subject is at least the limit set by this variable. */
  25. export int subject_match_limit = 20;     /* "strncmp" limit for subjects */
  26. #else
  27. /* Subjects are considered identical if their first s-m-l characters match */
  28. export int subject_match_limit = 256;     /* "strncmp" limit for subjects */
  29. #endif
  30.  
  31. export int match_skip_prefix = 0; /* skip first N bytes in matches */
  32. export int match_parts_equal = 0; /* match digits as equal */
  33. export int match_parts_begin = 4; /* require N characters for part match */
  34.  
  35. export int sort_mode = 5;        /* default sort mode */
  36.  
  37. extern int bypass_consolidation;
  38.  
  39. /*
  40.  *    String matching macroes.
  41.  *
  42.  *     MATCH_DROP(t, a) and MATCH_DROP(t, b) must both be proven false
  43.  *    before MATCH_?? (t, a, b) is used.
  44.  */
  45.  
  46. #ifdef HAVE_WORKING_COLLATE
  47.  
  48. #ifdef HAVE_8BIT_CTYPE
  49. #define MATCH_DROP(table, c) !isprint(c)
  50. #else
  51. #define MATCH_DROP(table, c) ( c & 0200 || !isprint(c) )
  52. #endif
  53. #define MATCH_EQ(table, a, b) ( a == b || table(a, b) == 0 )
  54. #define MATCH_LS_EQ(table, a, b) ( a == b || table(a, b) <= 0 )
  55. #define MATCH_LS(table, a, b) ( table(a, b) < 0 )
  56. #define MATCH_CMP(table, a, b) table(a, b)
  57.  
  58. static int match_subject(a, b)
  59. char a, b;
  60. {
  61.     static char aa[2], bb[2];
  62.  
  63.     aa[0] = a; bb[0] = b;
  64.     return strcoll(aa, bb);
  65. }
  66.  
  67. #else
  68.  
  69. #define    MATCH_DROP(table, c) ( c & 0200 || table[c] == 0 )
  70. #define MATCH_EQ(table, a, b) ( a == b || table[a] == table[b] )
  71. #define MATCH_LS_EQ(table, a, b) ( a <= b || table[a] <= table[b] )
  72. #define MATCH_LS(table, a, b) ( a < b || table[a] < table[b] )
  73. #define    MATCH_CMP(table, a, b) (table[a] - table[b])
  74.  
  75. static char match_subject[128] = {
  76.  
  77. /*  NUL SOH STX ETX EOT ENQ ACK BEL BS  TAB NL  VT  FF  CR  SO  SI  */
  78.     00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
  79.  
  80. /*  DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM  SUB ESC FS  GS  RS  US  */
  81.     00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
  82.  
  83. /*  SP  !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
  84.     00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 99, 00, 00, 00, 00,
  85. /*                                              ^^                  */
  86.  
  87. /*  0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
  88.      1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 00, 00, 00, 00, 00, 00,
  89.  
  90. /*  @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
  91.     00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
  92.  
  93. /*  P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
  94.     26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00,
  95.  
  96. /*  `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
  97.     00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
  98.  
  99. /*  p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~   DEL */
  100.     26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00
  101. };
  102.  
  103. #endif /* HAVE_WORKING_COLLATE */
  104.  
  105. static int
  106. order_thread_subj_date(ah1, ah2)
  107. article_header **ah1, **ah2;
  108. {
  109.   int n;
  110.   article_header *ath1 = (**ah1).thread_head_ptr;
  111.   article_header *ath2 = (**ah2).thread_head_ptr;
  112.   if (!ath1)
  113.     ath1 = *ah1;
  114.   if (!ath2)
  115.     ath2 = *ah2;
  116.  
  117.   n = order_subj_date(&ath1, &ath2);
  118.   if (n == 0) {
  119.     if ((**ah1).thread_cnt < (**ah2).thread_cnt) {
  120.       return -1;
  121.     } else if ((**ah1).thread_cnt > (**ah2).thread_cnt) {
  122.       return 1;
  123.     } else
  124.       return 0;
  125.   } else {
  126.     return n;
  127.   }
  128. }
  129.  
  130. static int
  131. order_thread_date(ah1, ah2)
  132. article_header **ah1, **ah2;
  133. {
  134.   int n;
  135.   article_header *ath1 = (**ah1).thread_head_ptr;
  136.   article_header *ath2 = (**ah2).thread_head_ptr;
  137.   if (!ath1)
  138.     ath1 = *ah1;
  139.   if (!ath2)
  140.     ath2 = *ah2;
  141.  
  142.   n = order_date(&ath1, &ath2);
  143.   if (n == 0) {
  144.     if ((**ah1).thread_cnt < (**ah2).thread_cnt) {
  145.       return -1;
  146.     } else if ((**ah1).thread_cnt > (**ah2).thread_cnt) {
  147.       return 1;
  148.     } else
  149.       return 0;
  150.   } else {
  151.     return n;
  152.   }
  153. }
  154.  
  155. static int
  156. order_subj_date(ah1, ah2)
  157. article_header **ah1, **ah2;
  158. {
  159.     register char *a = (**ah1).subject, *b = (**ah2).subject;
  160.     register char ca, cb;
  161.     register int p, len;
  162.  
  163.     if (match_skip_prefix) {
  164.     if ((int)(**ah1).subj_length > match_skip_prefix) {
  165.         if ((int)(**ah2).subj_length > match_skip_prefix) {
  166.         a += match_skip_prefix;
  167.         b += match_skip_prefix;
  168.         } else
  169.         return 1;
  170.     } else {
  171.         if ((int)(**ah2).subj_length > match_skip_prefix) {
  172.         return -1;
  173.         }
  174.     }
  175.     }
  176.  
  177. #ifdef BAD_ORDER_SUBJ_DATE
  178.     for (len = 0; ; len++, a++, b++) {
  179. #else
  180.     for (len = subject_match_limit; --len >= 0; a++, b++) {
  181. #endif
  182.     while ((ca = *a) && MATCH_DROP(match_subject, ((int8)ca))) a++;
  183.     while ((cb = *b) && MATCH_DROP(match_subject, ((int8)cb))) b++;
  184.  
  185.     if (ca == NUL) {
  186.         if (cb == NUL) break;
  187. #ifdef BAD_ORDER_SUBJ_DATE
  188.         if (len >= subject_match_limit) break;
  189. #endif
  190.         return -1;
  191.     }
  192.  
  193.     if (cb == NUL) {
  194. #ifdef BAD_ORDER_SUBJ_DATE
  195.         if (len >= subject_match_limit) break;
  196. #endif
  197.         return 1;
  198.     }
  199.  
  200.     if ((p = MATCH_CMP(match_subject, (int8)ca, (int8)cb))) return p;
  201.     }
  202.  
  203.     if ((**ah1).t_stamp > (**ah2).t_stamp) return 1;
  204.     if ((**ah1).t_stamp < (**ah2).t_stamp) return -1;
  205.     return order_arrival(ah1, ah2);
  206. }
  207. /* data_subj_date can only be used after root_t_stamp is set */
  208.  
  209. static int
  210. order_date_subj_date(ah1, ah2)
  211. article_header **ah1, **ah2;
  212. {
  213.     register time_stamp t1 = (**ah1).root_t_stamp;
  214.     register time_stamp t2 = (**ah2).root_t_stamp;
  215.  
  216.     if (t1 > t2) return 1;
  217.     if (t1 < t2) return -1;
  218.     return order_subj_date(ah1, ah2);    /* get original order */
  219. }
  220.  
  221.  
  222. static int
  223. order_arrival(a, b)
  224. article_header **a, **b;
  225. {
  226.     register long i;
  227.  
  228.     if ((i = (int)((*a)->a_number - (*b)->a_number)) == 0)
  229.     i = (*a)->fpos - (*b)->fpos;
  230.  
  231.     return (i > 0) ? 1 : (i < 0) ? -1 : 0;
  232. }
  233.  
  234. static int
  235. order_date(ah1, ah2)
  236. register article_header **ah1, **ah2;
  237. {
  238.     if ((**ah1).t_stamp > (**ah2).t_stamp) return 1;
  239.     if ((**ah1).t_stamp < (**ah2).t_stamp) return -1;
  240.     return order_arrival(ah1, ah2);
  241. }
  242.  
  243. static int
  244. order_from_date(ah1, ah2)
  245. register article_header **ah1, **ah2;
  246. {
  247.     register int i;
  248.  
  249.     if ((i = strcmp((**ah1).sender, (**ah2).sender))) return i;
  250.     return order_date(ah1, ah2);
  251. }
  252.  
  253. static flag_type article_equal(ah1, ah2) /* ah1.hdr == ah2.hdr */
  254. article_header **ah1, **ah2;
  255. {
  256.   /* thread   */
  257.   /*    register char *a = (**ah1).subject, *b = (**ah2).subject;*/
  258.     register char *a, *b;
  259.     register int len;
  260.  
  261.     if ((**ah1).thread_head_ptr)
  262.       a = (**ah1).thread_head_ptr->subject;
  263.     else
  264.       a = (**ah1).subject;
  265.     if ((**ah2).thread_head_ptr)
  266.       b = (**ah2).thread_head_ptr->subject;
  267.     else
  268.       b = (**ah2).subject;
  269.  
  270.     if (match_skip_prefix) {
  271.     if ((int)(**ah1).subj_length > match_skip_prefix) {
  272.         if ((int)(**ah2).subj_length > match_skip_prefix) {
  273.         a += match_skip_prefix;
  274.         b += match_skip_prefix;
  275.         }
  276.     }
  277.     }
  278.  
  279.     for (len = 0;; len++, a++, b++) {
  280.     while (*a && MATCH_DROP(match_subject, (int8)*a)) a++;
  281.     while (*b && MATCH_DROP(match_subject, (int8)*b)) b++;
  282.  
  283.     if (*a == NUL) {
  284.         if (*b == NUL) break;
  285.         if (len >= subject_match_limit) return A_ALMOST_SAME;
  286.         return 0;
  287.     }
  288.  
  289.     if (*b == NUL) {
  290.         if (len >= subject_match_limit) return A_ALMOST_SAME;
  291.         return 0;
  292.     }
  293.  
  294.     if (!MATCH_EQ(match_subject, (int8)*a, (int8)*b)) {
  295.         if (len >= subject_match_limit)
  296.         return A_ALMOST_SAME;
  297.         if (len >= match_parts_begin && match_parts_equal &&
  298.                isdigit(*a) && isdigit(*b))
  299.         return A_ALMOST_SAME;
  300.         return 0;
  301.     }
  302.     }
  303.  
  304.     return A_SAME;
  305. }
  306.  
  307. void
  308. sort_articles(mode)
  309. int mode;
  310. {
  311.     register article_header **app;
  312.     register long n;
  313.     register flag_type same;
  314.     fct_type cmp;
  315.  
  316.     if (n_articles <= 0) return;
  317.  
  318.     for (n = n_articles; --n >= 0;)
  319.     articles[n]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME|A_ROOT_ART);
  320.  
  321.     if (n_articles == 1) {
  322.     articles[0]->flag |= A_ROOT_ART;
  323.     return;
  324.     }
  325.  
  326.     if (mode == -1) mode = sort_mode;
  327.  
  328.     switch (mode) {
  329.      case -2:            /* restore original ordering for update */
  330.     cmp = order_arrival;
  331.     break;
  332.      default:
  333.      case 0:            /* arrival (no sort) */
  334.     cmp = order_arrival;
  335.     bypass_consolidation = 1;
  336.     break;
  337.      case 1:            /* date-subject-date */
  338.      case 2:            /* subject-date */
  339.     cmp = order_subj_date;
  340.     break;
  341.      case 3:            /* date only */
  342.     cmp = order_date;
  343.     bypass_consolidation = 1;
  344.     break;
  345.      case 4:            /* sender-date */
  346.     cmp = order_from_date;
  347.     bypass_consolidation = 1;
  348.     break;
  349.      case 5:            /* thread-subject-date */
  350.         cmp = order_thread_subj_date;
  351.         break;
  352.      case 6:            /* thread-date */
  353.         cmp = order_thread_date;
  354.         break;
  355.     }
  356.  
  357.     quicksort(articles, n_articles, article_header *, cmp);
  358.  
  359.     articles[0]->root_t_stamp = articles[0]->t_stamp;
  360.     articles[0]->flag |= A_ROOT_ART;
  361.     for (n = n_articles - 1, app = articles + 1; --n >= 0; app++) {
  362.     if ((same = article_equal(app, app - 1))) {
  363.         app[0]->root_t_stamp = app[-1]->root_t_stamp;
  364.         app[0]->flag |= same;
  365.         app[-1]->flag |= A_NEXT_SAME;
  366.     } else {
  367.         app[0]->root_t_stamp = app[0]->t_stamp;
  368.         app[0]->flag |= A_ROOT_ART;
  369.     }
  370.     }
  371.  
  372.     if (mode == 1)
  373.     quicksort(articles, n_articles, article_header *, order_date_subj_date);
  374. }
  375.  
  376. /*
  377.  *    If articles are not sorted via sort_articles, they must still be
  378.  *    marked with proper attributes (e.g. A_ROOT_ART) via no_sort_articles.
  379.  */
  380.  
  381. void
  382. no_sort_articles()
  383. {
  384.     register article_number n;
  385.     register article_header *ah;
  386.  
  387.     for (n = n_articles; --n >= 0;) {
  388.     ah = articles[n];
  389.     ah->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME|A_ROOT_ART);
  390.     ah->flag |= A_ROOT_ART;
  391.     }
  392.     bypass_consolidation = 1;
  393. }
  394.  
  395. /*
  396.  * Eliminate articles with the A_KILL flag set preserving the present ordering.
  397.  * This will only release the last entries in the articles array.
  398.  * Neither strings nor articles headers are released.
  399.  */
  400.  
  401. int
  402. elim_articles(list, list_lgt)
  403. register article_number *list;
  404. int list_lgt;
  405. {
  406.     register article_header **srca, **desta;
  407.     register article_number n, count;
  408.     register flag_type same;
  409.     int changed, llen;
  410.  
  411.     count = 0;
  412.     changed = 0, llen = 0;
  413.     for (n = 0, srca = desta = articles; n < n_articles; n++, srca++) {
  414.     if ((*srca)->attr == A_KILL) {
  415.         if (list_lgt > 0) {
  416.         if (n < *list) {
  417.             if (llen) changed = 1;
  418.         } else
  419.         if (n == *list) {
  420.             if (llen) {
  421.             llen++;
  422.             list_lgt--;
  423.             *list++ = -1;
  424.             } else
  425.             ++(*list);
  426.             changed = 1;
  427.         }
  428.         }
  429.         continue;
  430.     }
  431.     if (list_lgt > 0 && n == *list) {
  432.         *list++ = count;
  433.         list_lgt--;
  434.         llen++;
  435.     }
  436.     count++;
  437.     *desta++ = *srca;
  438.     }
  439.     if (list_lgt > 0) {
  440.     if (!llen) *list = 0;
  441.     changed = 1;
  442.     }
  443.     n_articles = count;
  444.  
  445.     if (changed && n_articles > 0) {
  446.     srca = articles;
  447.     srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  448.     srca[0]->flag |= A_ROOT_ART;
  449.     for (n = n_articles - 1, srca++; --n >= 0; srca++) {
  450.         srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME|A_ROOT_ART);
  451.         if ((same = article_equal(srca, srca - 1))) {
  452.         srca[0]->flag |= same;
  453.         srca[-1]->flag |= A_NEXT_SAME;
  454.         } else
  455.         srca[0]->flag |= A_ROOT_ART;
  456.     }
  457.     }
  458.  
  459.     return changed;
  460. }
  461.